1 //+-----------------------------------------------------------------------
3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // Deployment progress page. See .hxx.
9 // 2007/12/xx [....] Created
11 //------------------------------------------------------------------------
13 #include "Precompiled.hxx"
14 #include "ProgressPage.hxx"
15 #include "DllMain.hxx"
16 #include "OleDocument.hxx"
17 #include "HTMLPageHost.hxx"
18 #include "Utilities.hxx"
19 #include "psapi.h" // For GetMappedFileName()
22 CProgressPage::~CProgressPage()
24 // Normally, the page should be explicitly hidden before interfaces are released.
25 // Hide() is called here to help ensure orderly cleanup, just in case.
29 void CProgressPage::Init(IProgressPageHost
*pHost
)
31 ASSERT(!m_pHost
&& pHost
);
35 HRESULT
GetFilePathFromDevicePath(__in_ecount(MAX_PATH
) LPCTSTR pszDevicePath
, __out_ecount(MAX_PATH
) LPTSTR pszFilePath
)
37 // Translate path with device name to drive letters.
39 // Originally we tried to use GetLogicalDriveStrings/QueryDosDevice to map the
40 // path but on server 2003 this fails with an access denied error in QueryDosDevice.
41 // This is the way suggested by MSDN here: http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
43 // So, since (at least for this release) we always get installed to the Windows directory
44 // we can use that as a basis for some string manipulation to convert the device path
49 wchar_t *pszWindowsDirName
= pszFilePath
;
50 wchar_t szDevicePath
[MAX_PATH
];
51 CKHR(StringCchCopy(szDevicePath
, MAX_PATH
, pszDevicePath
)); // a copy that I can lower case
53 // 1) get windows directory, remove drive letter
54 CKHR(SHGetFolderPath(NULL
, CSIDL_WINDOWS
, NULL
, 0, pszFilePath
));
55 pszWindowsDirName
+= 3; // trim "c:\"
57 _wcslwr_s(pszFilePath
, MAX_PATH
);
58 _wcslwr_s(szDevicePath
, MAX_PATH
);
60 // 2) find the windows directory name in device path
61 const wchar_t* pszPath
= wcsstr(szDevicePath
, pszWindowsDirName
);
67 // 3) append path (including windows dir) on to original drive letter
68 *pszWindowsDirName
= '\0';
69 CKHR(StringCchCat(pszFilePath
, MAX_PATH
, pszPath
));
75 HRESULT
CProgressPage::Show()
81 HMODULE hModule
= NULL
;
82 CComPtr
<IUnknown
> spContainerUnknown
;
83 CComPtr
<IMoniker
> spMoniker
;
84 CComPtr
<IBindCtx
> spBindCtx
;
86 m_hHostWindow
= m_pHost
->GetHostWindow();
89 CKHR(CHTMLPageHostingHelper::CreateHTMLDocumentControl(m_hHostWindow
, 0, &spContainerUnknown
, &m_spHtmlDocument
));
90 m_spControlHost
= spContainerUnknown
;
92 // Load the progress page from res:// URL via IPersistMoniker. If IHTMLDocument2::put_URL() is called
93 // instead, IE 7 decides to create a new top-level window!
95 wchar_t progressPageURL
[MAX_PATH
] = L
"res://";
98 // On WinXP, LoadMUILibrary (and thus LoadResourceDLL) return an HMODULE which
99 // is a handle to a memory mapped file, not a "real" HMODULE. Because of
100 // this we have to jump through some hoops to get the fully qualified filename
104 // Load the associated resource DLL so that we can get its path via GetMappedFileName,
105 // this returns us a device path, so convert the device path to a file path.
106 hModule
= LoadResourceDLL();
107 CHECK_NULL_FROM_WIN32(hModule
);
108 wchar_t devicePath
[MAX_PATH
];
109 CHECK_ZERO_FROM_WIN32(GetMappedFileName(GetCurrentProcess(), hModule
, devicePath
, MAX_PATH
));
110 wchar_t filePath
[MAX_PATH
];
111 CKHR(GetFilePathFromDevicePath(devicePath
, filePath
));
113 // The MUI library gets loaded as a memory mapped file, so we want to
114 // free it here and call LoadLibrary() on the actual file so that IE
115 // can load resources from the root file path.
116 FreeMUILibrary(hModule
);
118 m_hostDllMuiModule
= LoadLibrary(filePath
);
119 CHECK_NULL_FROM_WIN32(m_hostDllMuiModule
);
121 CKHR(StringCchCat(progressPageURL
, MAX_PATH
, filePath
));
122 CKHR(StringCchCat(progressPageURL
, MAX_PATH
, L
"/ProgressPage.html"));
123 CKHR(CreateURLMoniker(NULL
, progressPageURL
, &spMoniker
));
125 CKHR(CreateBindCtx(0, &spBindCtx
));
126 CKHR(CComQIPtr
<IPersistMoniker
>(m_spHtmlDocument
)->Load(true/*try [....]*/, spMoniker
, spBindCtx
, 0));
128 CKHR(CComQIPtr
<IHTMLDocument
>(m_spHtmlDocument
)->get_Script(&m_spDocumentScript
));
129 CKHR(m_spControlHost
->SetExternalDispatch(static_cast<IDispatch
*>(this)));
135 FreeMUILibrary(hModule
);
139 m_bFailed
= FAILED(hr
);
143 HRESULT
CProgressPage::Hide()
145 if(m_spHtmlDocument
== NULL
) // Hide even if we failed to initialize completely
151 hr
= m_spControlHost
->AttachControl(NULL
, NULL
);
153 m_spControlHost
.Release();
154 m_spHtmlDocument
.Release();
155 m_spDocumentScript
.Release();
157 if (m_hostDllMuiModule
)
159 FreeLibrary(m_hostDllMuiModule
);
160 m_hostDllMuiModule
= NULL
;
163 // **BEWARE**: This call may cause all interface references to be released, which will delete the object.
166 m_pHost
->OnProgressPageClosed();
172 HRESULT
CProgressPage::TranslateAcceleratorIO(__in MSG
*pMsg
)
174 if(m_spHtmlDocument
!= NULL
)
176 // When focus is on the progress page, we want at least the basic navigation keys to work.
177 // The hosted HTMLDocument object and the top-level browser are not aware of each other...
178 if(CHTMLPageHostingHelper::HandleNavigationKeys(*pMsg
))
181 // Crude heuristic: inferring when the HTML page is shown. There is no notification about that.
182 // The DHTML onload event usually occurs earlier. We want the progress page to be really shown
183 // before we get bogged down in loading the managed assemblies, which on cold start takes a long time.
184 // The HTMLDocument window will necessarily get WM_PAINT so that it can show anything. It may, however,
185 // not render all the content in response to the first WM_PAINT, because it loads and parses content
186 // asynchonously. Practically, it seems to show the basic page text on first render, and then the
187 // images may come a little later. This works out just fine for us.
188 // See also COleObject::DoVerb().
189 if(!m_bRendered
&& pMsg
->message
== WM_PAINT
&& GetParent(pMsg
->hwnd
) == m_hHostWindow
)
191 m_bPartialRendered
= true; //(Used for debugging if nothing else.)
195 EventWriteWpfHostUm_ProgressPageShown();
196 m_pHost
->OnProgressPageRendered();
203 //[IDispatch implementation begin]
205 // Notifications coming from script in the HTML page
206 const LONG DISPID_CANCEL
= 7;
207 const LONG DISPID_ONLOADED
= 8;
209 HRESULT
CProgressPage::GetIDsOfNames(
210 REFIID
, __in_ecount(cNames
) LPOLESTR
*pNames
, UINT cNames
, LCID
, __out_ecount(cNames
) DISPID
* rgDispId
)
214 if(wcscmp(pNames
[0], L
"OnLoaded") == 0)
216 *rgDispId
= DISPID_ONLOADED
;
219 if(wcscmp(pNames
[0], L
"Cancel") == 0)
221 *rgDispId
= DISPID_CANCEL
;
225 return DISP_E_UNKNOWNNAME
;
228 STDMETHODIMP
CProgressPage::Invoke(DISPID dispidMember
,
229 __in_ecount(1) REFIID
, LCID
, WORD
, __in_ecount(1) DISPPARAMS
*,
230 __out_ecount(1) VARIANT
* pVarResult
,
231 __out_ecount_opt(1) EXCEPINFO
* pExcepInfo
, __out_ecount_opt(1) UINT
* puArgErr
)
235 case DISPID_ONLOADED
:
237 // Note this is only 'logical load'. The page is likely not rendered yet. See the WM_PAINT detection.
240 return m_pHost
->ExecOLECommand(OLECMDID_STOP
);
242 return DISP_E_MEMBERNOTFOUND
;
246 //[IDispatch implementation end]
248 // "Nonstandard extension used : class rvalue used as lvalue"
249 // This warning occurs when passing &CComVariant(value) as a method parameter.
250 // It's awkward to declare local varibles for this. Suppressing the warning is safe for input-only VARIANTs.
251 #pragma warning(disable: 4238)
253 HRESULT
CProgressPage::ShowProgressMessage(BSTR message
)
257 ASSERT(m_bLoaded
); // If not, we'll get DISP_E_UNKNOWNNAME below.
258 return m_spDocumentScript
.Invoke1(L
"ShowProgressMessage", &CComVariant(message
));
261 HRESULT
CProgressPage::SetApplicationName(BSTR appName
)
265 ASSERT(m_bLoaded
); // If not, we'll get DISP_E_UNKNOWNNAME below.
266 return m_spDocumentScript
.Invoke1(L
"SetApplicationName", &CComVariant(appName
));
269 HRESULT
CProgressPage::SetPublisherName(BSTR publisherName
)
273 return m_spDocumentScript
.Invoke1(L
"SetPublisherName", &CComVariant(publisherName
));
276 HRESULT
CProgressPage::OnDownloadProgress(UINT64 bytesDownloaded
, UINT64 bytesTotal
)
280 // Passing 64-bit ints to script (or via IDispatch) doesn't seem to work.
281 // That's why the contract is to pass KB.
282 return m_spDocumentScript
.Invoke2(L
"OnDownloadProgress",
283 &CComVariant(UINT((bytesDownloaded
+512)/1024)), &CComVariant(UINT((bytesTotal
+512)/1024)));